Kernel.module.define('navigation', {
	
	title: 'navigation',
	//file: 'navigation.php',
	widget: true,
	EARTH_RADIUS: 6378137,
	DEG_TO_RAD_FACTOR: Math.PI / 180,
	
	init: function() {
		var module = this;
		var gpsStarted = false;
		var routeData;
		var navMode = 0;
		var marker = '';
		var oldPacket = '';
		var oldTbtUpdate = '';
		var mapTimer = '';
		var closestPointIndex = 0;
		var closestDirectionIndex = 0;
		var lp = [];
		var lpCount = 0;
		var currentSpeed = 0;
		var currentAltitude = 0;
		var lastGPSPosition = (localStorage.getItem("CONFIG_LAST_GPS_POSITION") !== null) ? localStorage.getItem("CONFIG_LAST_GPS_POSITION"):"0:0";
		var GPSStartPosition = lastGPSPosition.split(":");
		var configTileServer = (localStorage.getItem("CONFIG_GPS_TILE_SERVER") !== null) ? localStorage.getItem("CONFIG_GPS_TILE_SERVER"):"";
		var configRouteServer = (localStorage.getItem("CONFIG_GPS_ROUTE_SERVER") !== null) ? localStorage.getItem("CONFIG_GPS_ROUTE_SERVER"):"";
		
		L.Icon.Default.imagePath = 'widgets/navigation/images/leaflet/';

		var map = L.map('dashNavigationLyr', {
		  minZoom: 5,
		  maxZoom: 18
		});
		 
		map.setView([GPSStartPosition[0],GPSStartPosition[1]], 17);
		var tiles = configTileServer;
		var layer = L.tileLayer(tiles);
		var scale = L.control.scale({
			position: 'bottomright',
			maxWidth: 100,
			metric: true,
			imperial: false
		});

		var mIcon = L.icon({
			iconUrl: 'widgets/navigation/images/leaflet/marker-icon.png',
			
			iconSize: [50, 50]
		});
		
		layer.addTo(map);
		scale.addTo(map);
		
		$(".mainContent").append("<div id='speedNaltitude' style='background-color:#fff; opacity:0.5;"+
								"font-size:0.7em; color:#000; width:90px; height:32px; margin-top:5px; margin-left:5px; "+
								"position:absolute; top:"+($("#dashNavigationLyr").height()-85)+"px; left:"+($("#dashNavigationLyr").width()-100)+"px;'></div>");

		$(".mainContent").append("<div id='nextturn' style='position:absolute; top:10px; left:50px;"+
		"opacity: 0.8; vertical-align=top; background-color: #00aa00; width: "+($("#dashNavigationLyr").width()-100)+"px; height: 90px;'>"+
			"<div id='tbtWrapper' style='width: "+($("#dashNavigationLyr").width()-111)+"px; height: 80px; border:2px; border-style:solid; border-color:#fff;  margin-left:3px; margin-top: 3px; margin-right: 3px;'>"+
				"<div id='tbtImage' style='float: left; text-align:center; width: 70px; font-size: 1em;'></div>"+
				"<div id='tbtContent' style='float:left; valign:top; font-size:1.3em;  margin-left:5px; margin-top: 5px; margin-right: 5px;'></div>"+
		"</div></div>");
		$(".mainContent").append("<div id='nextturntotal' style='position:absolute; top:100px; left:50px; opacity: 0.8;background-color: #fff; "+
							"width: "+($("#dashNavigationLyr").width()-100)+"px; height: 20px;"+
							"font-size: 1.5em; color: #000;'></div>");
		$("#nextturn").hide();
		$("#nextturntotal").hide();
		$("#tbtContent").css("width",  $("#dashNavigationLyr").width()-195);

		$(document).ready(function() {
			$("#accordion").append("<div class='group'><h3>Itinerary</h3><div id='turnbyturn'></div></div>");
			$("#accordion").accordion("refresh");
		});


		$("#nextturn").on('click', function() {
			// toggle accordion turn-by-turn panel
		});
		

		//////////////////////////////////////////////////////////////
		// events
		//////////////////////////////////////////////////////////////
		$('.leaflet-container').css('cursor', 'crosshair');

        map.on("movestart", function(m) {
			var cDate = new Date();
			mapTimer = new Date(cDate.getTime() + (30 * 1000));
		});
		
		map.on("click", function(m) {
			if (navMode > 0) {
				// user cancels turn-by-turn
				closestPointIndex = 0;
				closestDirectionIndex = 0;
				map.removeLayer(line);
				oldTbtUpdate = 0;
				$("#accordion").accordion({active: 0});
				$("#accordion").accordion("refresh");
				$("#nextturn").hide();
				$("#nextturntotal").hide();
				$("#turnbyturn").html("");
				$("#turnbyturn").accordion().children('.ui-accordion-header:eq(2)').hide();
				navMode = 0;
			} else {
				if (gpsStarted) {
					// start turn-by-turn
					$("#nextturn").show();
					$("#nextturntotal").show();
					$("#tbtImage").html("");
					$("#tbtContent").html("Waiting for GPS...");
					$("#turnbyturn").accordion().children('.ui-accordion-header:eq(2)').show();
					var cDate = new Date();
					mapTimer = new Date(cDate.getTime() + (35 * 1000));
					
					var sp = module.sandbox.vehiclePosition();
					sp = sp.split(":");
					lp = [[parseFloat(sp[0]),parseFloat(sp[1])]];

					var popLocation = m.latlng;
					var destLocationLat = popLocation.lat;
					var destLocationLng = popLocation.lng;
					var vehicleHeading = module.sandbox.vehicleCourse().toFixed(0);
					
					// call graphhopper for directions
					var routeURL = "?point="+sp[0]+"%2C"+sp[1]+"&point="+destLocationLat+"%2C"+destLocationLng+"&locale=en-GB&vehicle=car&weighting=fastest&elevation=false&layer=Omniscale&points_encoded=false";
					$.get(configRouteServer + routeURL, function(data) {
						var totalDistance = (data.paths[0].distance < 1000) ? data.paths[0].distance.toFixed(2) + "m" : (data.paths[0].distance/1000).toFixed(2) + "km";
						routeData = data;
						lpCount = data.paths[0].points.coordinates.length;
					
						var routeDistance = data.paths[0].distance;
						var routeDistanceMetric = "";
						if (routeDistance < 1000) {
							routeDistanceMetric = "m";
						} else {
							routeDistanceMetric = "km";
							routeDistance = routeDistance / 1000;
						}
						var routeTime = data.paths[0].time;
						var opt = {
							color: 'blue',
							weight: 5,
							opacity: 0.5,
							smoothFactor: 1
						};

						// add all point coordinates to lp array
						for (i = 0; i < lpCount; i++) {
							lp = lp.concat([[data.paths[0].points.coordinates[i][1], data.paths[0].points.coordinates[i][0]]]);
						}
						
						// print all turn by turn directions
						module.printInstructions(1, routeData, "--", "--");
						
						$("#accordion").accordion({active: 3});
						$("#accordion").accordion("refresh");

						line = new L.polyline(lp, opt);
						line.addTo(map);
						navMode = 1;
					});
				}
			}
		});
		
		module.sandbox.listen('gps-position', function(data) {
 			if (data !== "" && oldPacket !== data) {
				gpsStarted = true;
				var cDate = new Date();
				var mt = new Date(cDate.getTime());
				var latlon = data.split(":");
				var lastUsedGPSPosition = localStorage.setItem("CONFIG_LAST_GPS_POSITION", data);

				$("#speedNaltitude").show();
				$("#speedNaltitude").on('click', function(e) {
					map.setView([latlon[0], latlon[1]]);
					mapTimer = '';
				});

				if (mapTimer == '' || mt > mapTimer) {
					map.setView([latlon[0], latlon[1]]);
					mapTimer = '';
				} else {
					map.invalidateSize();
				}
				map.removeLayer(marker);
				marker = new L.marker([latlon[0], latlon[1]], {icon: mIcon}).addTo(map);
				oldPacket = data;
				
				$("#speedNaltitude").html("Spd: <b>" + currentSpeed + "</b><br>Alt: <b>" + currentAltitude+"</b>");
				
				// if in navigation mode, calculate turn-by-turn
				if (navMode > 0) {
					var lastDist = 999999;
					var directionIndex = 0;
					var pathStart = closestPointIndex;
					var pathLen = routeData.paths[0].points.coordinates.length - 1;
					var maxIterations = (lastDist > 35) ? pathLen:((pathLen - pathStart) > 5) ? 5:pathLen
					
					for (i = pathStart; i < maxIterations; i++) {
						var point = module.closestOnLine(routeData.paths[0].points.coordinates[i], routeData.paths[0].points.coordinates[(i + 1)], [parseFloat(latlon[1]), parseFloat(latlon[0])]);
						var dist = module.distance([latlon[0], latlon[1]], point);
						
						if (lastDist < dist) continue
						
						lastDist = dist;
						closestPointIndex = i;
					}
					
					if (lastDist > 35) {
						// we are off-route
						$("#tbtImage").html("");
						$("#tbtContent").html("You are Off-Route!");
						$("#nextturntotal").hide();
						closestPointIndex = 0;
						oldTbtUpdate = 0;
					} else {
						// find where we are on route
						$("#nextturntotal").show();
						var instructLen = routeData.paths[0].instructions.length;
						var distLeft = 0;
						var routeComplete = false;
						for (i = closestDirectionIndex; i < instructLen-1; i++) {
							var minPoint = routeData.paths[0].instructions[i].interval[0];
							var maxPoint = routeData.paths[0].instructions[i].interval[1];
							distLeft = distLeft + routeData.paths[0].instructions[i].distance;
							
							if (closestPointIndex >= minPoint && closestPointIndex <= maxPoint) {
								closestDirectionIndex = i;
								continue;
							}
						}

						// calculate ETA and distance left
						var totalDistLeft = 0;
						var totalTimeLeft = 0;
						for (i = closestDirectionIndex; i < instructLen; i++) {
							totalDistLeft = totalDistLeft + routeData.paths[0].instructions[i].distance;
							totalTimeLeft = totalTimeLeft + (routeData.paths[0].instructions[i].time/1000);
						}
						var totalDistLeftTxt = (totalDistLeft < 1000) ? totalDistLeft.toFixed(0) + "m":(totalDistLeft/1000).toFixed(0) + "km";

						// update turn-by-turn
						if (oldTbtUpdate == 0 || closestDirectionIndex !== oldTbtUpdate) {
							module.printInstructions((closestDirectionIndex + 1), routeData, totalDistLeftTxt, totalTimeLeft.toFixed(0));
							oldTbtUpdate = closestDirectionIndex;
						}
							
						var distToDest = module.distance([latlon[0], latlon[1]], [routeData.paths[0].points.coordinates[routeData.paths[0].points.coordinates.length-1][1], routeData.paths[0].points.coordinates[routeData.paths[0].points.coordinates.length-1][0]]);
						if (distToDest <= 10) {
							// You have arrived at your destination
							closestPointIndex = 0;
							closestDirectionIndex = 0;
							map.removeLayer(line);
							oldTbtUpdate = 0;
							$("#accordion").accordion({active: 0});
							$("#accordion").accordion("refresh");
							$("#nextturn").hide();
							$("#nextturntotal").hide();
							$("#turnbyturn").html("");
							$("#turnbyturn").accordion().children('.ui-accordion-header:eq(2)').hide();
							navMode = 0;
						} else {
							var distToNextTurn = module.distance([latlon[0], latlon[1]], [routeData.paths[0].points.coordinates[routeData.paths[0].instructions[(closestDirectionIndex+1)].interval[0]][1], routeData.paths[0].points.coordinates[routeData.paths[0].instructions[(closestDirectionIndex+1)].interval[0]][0]]);
							var directionTxt = routeData.paths[0].instructions[(closestDirectionIndex+1)].text;
							directionTxt = directionTxt.replace('Street', 'St.');
							directionTxt = directionTxt.replace('Avenue', 'Ave.');
							directionTxt = directionTxt.replace('Road', 'Rd.');
							directionTxt = directionTxt.replace('Highway', 'Hwy.');
							directionTxt = directionTxt.replace('Parade', 'Pde.');
					    	directionTxt = directionTxt.replace('Bridge', 'Brg.');
							directionTxt = directionTxt.replace('Freeway', 'Fwy.');
							directionTxt = directionTxt.replace('Drive', 'Dr.');
							
							// only show next turn if closer than 3000m
							if (distToNextTurn < 3000) {
								var distToNextTurnTxt = (distToNextTurn < 1000) ? distToNextTurn + "m":(distToNextTurn/1000).toFixed(2) + "km";
								$("#nextturn").show();
								$("#nextturntotal").show();
								$("#tbtImage").html("<img id='nextturnimage' src='widgets/navigation/images/tbt/tbticon-"+
									routeData.paths[0].instructions[(closestDirectionIndex+1)].sign + ".png' width='50'><br>" + 
									distToNextTurnTxt);
								$("#tbtContent").html(directionTxt);
								$("#nextturntotal").html("<div style='float:left; width: 50%; font-size: 0.5em;'><b>ETA</b>: " + totalTimeLeft.toFixed(0) + "s</div>"+
														"<div style='float:left; width:50%; text-align: right; font-size: 0.5em;'><b>Dist. Left</b>: " + totalDistLeftTxt + "</div>");
							} else {
								$("#nextturn").hide();
								$("#nextturntotal").hide();
							}
						}
					}
				}
 			}
		});

		module.sandbox.listen('gps-nofix', function(data) {			
			$("#speedNaltitude").hide();
		});		

		module.sandbox.listen('gps-course', function(data) {			
			// rotate icon
		});		

		module.sandbox.listen('gps-speed', function(data) {
			currentSpeed = data;
		});
		
		module.sandbox.listen('gps-altitude', function(data) {
			currentAltitude = data;
		});
	},
	
	printInstructions: function(minStart, rData, totalDist, totalTime) {
		$("#turnbyturn").html("");

		for (i = minStart; i < rData.paths[0].instructions.length; i++) {
			var sign = rData.paths[0].instructions[i].sign;
			
			var instructText = rData.paths[0].instructions[i].text;
			if (sign !== 6) {
				instructText = instructText.split(",");
				instructText = instructText[0];
			}
			instructText = instructText.replace('Street', 'St.');
			instructText = instructText.replace('Avenue', 'Ave.');
			instructText = instructText.replace('Road', 'Rd.');
			instructText = instructText.replace('Highway', 'Hwy.');
			instructText = instructText.replace('Parade', 'Pde.');
			instructText = instructText.replace('Bridge', 'Brg.');
			instructText = instructText.replace('Freeway', 'Fwy.');
			instructText = instructText.replace('Drive', 'Dr.');

			if (i == 0) {
				$("#turnbyturn").append("<div style='float:left;width: 40px;'><img src='widgets/navigation/images/tbt/tbticon-"+rData.paths[0].instructions[i].sign+".png' width='30'></div>"+
										"<div style='float:left;'>"+instructText+"</div><br><hr>");
			} else if (i > 0) {
				var instructDistanceTxt = (rData.paths[0].instructions[(i-1)].distance > 1000) ? (
											rData.paths[0].instructions[(i-1)].distance/1000).toFixed(2) + "km" : 
											rData.paths[0].instructions[(i-1)].distance.toFixed(0) + "m";
				$("#turnbyturn").append("<div><div style='float:left; height: 60px;width:40px; text-align:center;'><img src='widgets/navigation/images/tbt/tbticon-"+rData.paths[0].instructions[i].sign+".png' width='30'><br>"+instructDistanceTxt+"<br></div>"+
										"<div style='float:left; height: 40px; margin-left: 10px;'>"+instructText+"</div></div><br><br><hr>");
			}
		}
		$("#turnbyturn").append("<div><div style='float:left; width:50%;'><b>" + totalDist + "</b> left.</div> <div style='float:left; align:right; width:50%;'><b>" + totalTime + "sec</b> left.</div></div>");
	},
	
	/**
	* Returns the distance between two LatLng objects in meters.
	*
	* Slightly modified version of the harversine formula used in Leaflet:
	*  https://github.com/Leaflet/Leaflet
	*/
	distance: function (p1, p2) {

		// convert degrees to radians
		var lat1 = p1[0] * this.DEG_TO_RAD_FACTOR;
		var lat2 = p2[0] * this.DEG_TO_RAD_FACTOR;
		var a = Math.sin(lat1) * Math.sin(lat2) +
			Math.cos(lat1) * Math.cos(lat2) * Math.cos((p2[1] - p1[1]) * this.DEG_TO_RAD_FACTOR);

		return parseInt(this.EARTH_RADIUS * Math.acos(Math.min(a, 1)));
	},

	/**
	* Returns the closest point on a line
	* to another given point.
	*
	*/
	closestOnLine: function(s1, s2, p){
		var x1 = s1[1], y1 = s1[0], x2 = s2[1], y2 = s2[0], px = p[1], py = p[0];
		var xDelta = x2 - x1;
		var yDelta = y2 - y1;

		//p1 and p2 cannot be the same point
		if ((xDelta === 0) && (yDelta === 0)) {
		  return s1;
		}

		var u = ((px - x1) * xDelta + (py - y1) * yDelta) / (xDelta * xDelta + yDelta * yDelta);

		var closestPoint;
		if (u < 0) {
		  closestPoint = [x1, y1];
		} else if (u > 1) {
		  closestPoint = [x2, y2];
		} else {
		  closestPoint = [x1 + u * xDelta, y1 + u * yDelta];
		}

		return ([closestPoint[0], closestPoint[1]]);
	},
	
	resizedone: function(map) {
	}
});
